#[macro_use]
extern crate log;
extern crate log4rs;

//use std::time;
//use std::thread;
//use std::sync::{Arc, Mutex};
//use std::time::Duration;

use crate::common::uks::student::Student;


// 由于main.rs不会被其他地方引用了，所以不需要用pub mod common;
// 这句话有点像Java idea里的添加“Module”依赖，所以Rust里的mod不应该和IDEA里的Module等价
// ，mod其实就是Java里的包的概念；，所以用IDEA写Rust容易混淆项目之间的Module和Rust名称空间的mod/或Java的package概念
// TODO 这一句话的意思是此rust文件里可以用common名称空间，然后后面就能够用common::xxx这种写法
// TODO 而后面的use common::uks则是类似静态导入，可以直接uks::mm()而不用common::uks::mm()
mod common;
// 感觉还是这种更符合直觉。。【和C++的很像】
//include!("common/uks");
// 注意，这里和Java不一样的是如果use common::uks那么就代表同时use了common，不能再use common;会报错
//use common::uks;
// 由于add是个方法或者struct之类的所以不会报错；
// 下面还可以写成use crate::common::uks::add;
//use common::uks::add;

/**
 * 当一个方法调用或宏调用或模块为域内最后一行/一部分代码时可以不加分号结束，但是这里都加上吧
 */
fn main() {
	let input = 1;
	if input == 2 {
		println!("uuuu");
	} else if input > 8 {
		println!("kkk");
	} else {
		println!("default");
	}

	let b = if true {
		3
	} else {
		4
	};
	println!("Hello, world! {}", b);

	let mut i = 1;
	loop {
		println!("Hello Yiibai");
		if i == 7 {
			break;
		}
		i += 1;
	}

	for i in 1..11 {
		print!("{} ", i);
	}

	let fruits = ["mango", "apple", "banana", "litchi", "watermelon"];
	for a in fruits.iter() {
		print!("{} ", a);
	}

	let mut i = 1;
	while i <= 10 {
		print!("{}", i);
		print!(" ");
		i = i + 1;
	}

	let v1 = vec![1, 2, 3];
	let v2 = v1;
	println!("{}, {}", v2[0], v2[1]);
	// 报错，因为v1的值的所有权移动给了v2，所以不能再访问v1

	let strmm: String = String::from("Yiibai是否");
	// 这里直接忽略了返回值，其实应该给个警告的，不过这个可以后期编译器来做提示，或者强制要求let _ 来主动声明忽略警告
	take_ownership(strmm);
	let ch = 'a';
	// TODO let mut重新赋值后之前的值哪去了？【因该是编译器判断到了重新赋值所以释放掉了】
	let kks = moves_copy(ch);
	println!("{}, {}", ch, kks);

	// 报错，因为String和整型，bool等不一样不是复制语义，而是移动语义，因此strmm的值的所有权转给了strnn
	//println!("{}", strmm);

	// TODO &mm是对mm变量的引用不取mm值（或简化叫mm的所有权）的所有权那么如果是在新的线程里执行这样的方法不会有数据竞争吗？

	let mut xx = String::from("kkksu");
	println!("{}", xx);
	// 这里其实从原理上可以不用&mut直接&即可，因为在上面已经声明了xx是mut的，但是为了显眼所以这里也要求同时给出
	value_changed(&mut xx);
	println!("After modifying, the value of x is {}", xx);

	let strr = String::from("Yiibai.com tutorial");
	// 注意这里取值为0到9这10个  字节，因此如果切片里有中文它可能一次性占据3个字节的，输出的字符个数少2个（-3 + 1）
	// ，码元长度为1字节；注意sbai其实可以理解为u8类型的字节数组，是在栈里的，类似ubyte[] sbai = {0x33,0xAA...}这种
	// 但是由于栈里的数据是不会自动释放的，要整个线程退出才释放，所以这是不是潜在的内存占用bug？，像int b = 0;这种虽然也不释放好歹只有8字节
	// 不过由于切片是不可变的，所以rust做了栈内享元，其他位置也可以引用相同的数据【切片没有所有权】
	let sbai = &strr[..=8];
	// 注意，sbai的类型是&str，很奇怪的类型，它和let mmm = "kkk";的mmm的类型一样；
	// 不过这里之所以不是&String类型，因为sbai它只代表字符串的一个切片而非strr这整个字符串；
	println!("first word of the given string is {}", sbai);

	// 注意，这里声明employee是mut主要是指employee引用的对象是不可变的（当然employee指向的地址index也是不可变的）
	let mut employee = Employee {
		employee_id: 88,
		employee_name: "silentdoer".to_owned(),
	};
	// 注意，employee必须是mut的，否则这里不允许改变
	employee.employee_name = String::from("sfjk");
	println!("{}", employee.employee_name);

	let emp2 = Employee {
		employee_id: 33,
		..employee
	};
	println!("{}", emp2.employee_name);
	// 报错，因为它的所有权已经移动到了emp2的employee_name里
	//println!("{}", employee.employee_name);
	// 这样就不会报错了，因为所有权移动回来了，由于emp2是不可变的，所以初始化后不支持再赋值；
	employee.employee_name = emp2.employee_name;
	println!("{}", employee.employee_name);

	println!("{}", employee.get_name());

	employee.set_name(33);
	println!("{}", employee.employee_id);
	println!("{}", employee.employee_name);

	let name = Developer::Name("silentdoer".to_string());
	let id = Developer::Id(3);
	println!("{:?}, {:?}", name, id);

	// 就是switch，个人感觉match比switch名字上更直观，self也比this直观
	match ch {
		'm' => println!("lll"),
		'a' => println!("kkk"),
		// 表示其他值
		_ => println!("default"),
	}

	// Some(a)是用于创建a的Option对象，None也是创建一个Option对象，None类似null
	// 其实C#里的int?就是Option<int>类型，然后Rust的JSON包应该会支持序列化和反序列化Option<T>属性
	// 而且对于Rojo的属性（和外部有交互的那部分）就应该用Option<T>类型而不能直接用T
	// Option只是有IO交互或者序列化之类的需要；
	let option = Some(3);

	println!("{}", common::uks::add(3, 4));
	println!("{}", common::uks::ek::sub(4, 3));
	common::test();

	println!("{}", Student { name: String::from("sjf是"), id: 33 });
	let student = Student::from("数据开放".to_string(), 99);

	println!("{}", student);

	//println!("{}", student.show());

	log4rs::init_file("config/log4rs.yaml", Default::default()).unwrap();
	error!("是否健康了444");

	//let thread1 = thread::current();
	//println!("{}", thread1.name().unwrap());

	// 必须处理返回值，如果实在不需要也要用let _ 来显式忽略
	// 这里move是当产生闭包时将外部变量的所有权移交到闭包里
	//let childThd = thread::Builder::new().name("child".to_string()).spawn(move || {
	//	println!("{}, {}", "新的线程".to_string(), thread::current().name().unwrap());
	//});

	//thread::sleep(Duration::from_secs(2));
}

fn take_ownership(strnn: String) -> String {
	println!("{}", strnn);
	strnn
}

// 注意，虽然8是整型，但是由于它是函数返回值，所以是移动语义
fn moves_copy(c: char) -> u32 {
	println!("{}", c);
	return 8;
}

fn value_changed(y: &mut String) {
	// 这种叫借用，上面的k: &String叫引用【引用在生命周期到了后不会释放其值[对应的对象]】
	// 这里用y和*y都可以，y就是xx的引用，而*y则是xx本身，TODO 这一块概念其实还要好好理解
	println!("{}", *y);
	// 注意，y是(mut String)这种类型的引用，但是y自己是immut的，所以这里不能这么赋值
	//y = String::from("uus");
	// 借用，这里相当于对y值的所有权的变量（xx）进行重新赋值，因此这里编译器会加上释放内存的代码；
	// 这句代码可以理解为xx = ...；【*y就类似C++里的外部xx变量的别名】
	// 别名的概念可以结合指针和指针内容及汇编来理解，比如指针333333【64位系统】在333333这个位置（类似数组的index）后面的8字节
	// 里的内容是88，即变量m的值是88，然后别名其实就是对同一块指针内容进行修改（指针是index，指针内容才是对应的值内容）；
	// 别名就是对同一块值内容进行修改；
	*y = String::from("test");
}

struct Employee {
	employee_name: String,
	employee_id: u64,
}

/* 注意，一个结构体可以没有实现只是用于存储数据 */
impl Employee {
	fn get_name(&self) -> String {
		// Rust这样拼接不知道会不会有问题。。不过既然是标准库就大胆用吧。。
		format!("{}{}", &self.employee_name, &self.employee_id)
	}

	pub fn set_name(&mut self, id: u64) {
		self.employee_id = id
	}
}

// 注意，Rust的枚举和其他语言很不一样，它的枚举常量的结构居然可以不一样（Name和Id）
// 不知道为什么这里必须加上这一句。。
#[derive(Debug)]
enum Developer {
	Name(String),
	Id(i32),
}